A programozási nyelvkönyvek elmagyarázzák, hogy az értéktípusok a veremben, a referencia típusok pedig a kupacban jönnek létre anélkül, hogy elmagyaráznák, mi ez a két dolog. Nem olvastam erről egyértelmű magyarázatot. Értem, mi az a verem. De, Hol és mik vannak (fizikailag a valódi számítógép memóriájában)? Mennyire ellenőrzi őket az operációs rendszer vagy a nyelv futási ideje? Mi a hatályuk? Mi határozza meg mindegyik méretét? Mi teszi az embert gyorsabbá?
2020-12-07 21:38:05
A verem a memória, amelyet a végrehajtási szál karcolására elkülönítenek. Funkció meghívásakor a verem tetején egy blokkot tartanak fenn a helyi változók és néhány könyvelési adat számára. Amikor ez a függvény visszatér, a blokk használatlanná válik, és a funkció legközelebbi meghívásakor használható. A verem mindig LIFO (utoljára az elsőben ki) sorrendben van fenntartva; a legutóbb lefoglalt blokk mindig a következő felszabadítandó blokk. Ez igazán egyszerűvé teszi a verem nyomon követését; blokk felszabadítása a veremből nem más, mint egy mutató beállítása. A halom a dinamikus allokációra elkülönített memória. A veremtől eltérően nincs kényszerített minta a blokkok kiosztására és elosztására a kupacból; bármikor kioszthat egy blokkot, és bármikor felszabadíthatja. Ez sokkal bonyolultabbá teszi annak nyomon követését, hogy a halom mely részei vannak kiosztva vagy szabadon bármikor; sok egyedi halomallokátor áll rendelkezésre a halom teljesítményének beállítására a különböző használati mintákhoz. Minden szál veremhez jut, míg az alkalmazáshoz általában csak egy halom van (bár nem ritka, hogy több halom van a különböző típusú allokációkhoz). A kérdésekre adott közvetlen válasz: Mennyire ellenőrzi őket az operációs rendszer vagy a nyelv futásideje? Az operációs rendszer a rendszer létrehozásakor minden rendszerszintű szálhoz lefoglalja a veremet. Az operációs rendszert általában a nyelv futásideje hívja meg, hogy lefoglalja a halmot az alkalmazás számára. Mi a hatályuk? A verem egy szálhoz van rögzítve, így amikor a szál kilép, a verem visszanyerésre kerül. A halmot általában az alkalmazás indításakor futtatja le a futás, és az alkalmazás (technikailag folyamat) kilépésekor igényli vissza. Mi határozza meg mindegyik méretét? A verem méretét egy szál létrehozásakor állítják be. A kupac méretét az alkalmazás indításakor állítják be, de nőhet, ha helyre van szükség (az allokátor több memóriát kér az operációs rendszertől). Mi teszi az embert gyorsabbá? A verem gyorsabb, mert a hozzáférési minta miatt triviális a memória lefoglalása és elosztása belőle (egy mutatót / egészet egyszerűen növelünk vagy csökkentünk), míg a halom sokkal bonyolultabb könyveléssel jár az allokációban vagy az elosztásban. Ezenkívül a veremben található egyes bájtokat nagyon gyakran használják, ami azt jelenti, hogy a processzor gyorsítótárához vannak hozzárendelve, ami nagyon gyors. A halom másik teljesítménysütője, hogy a halomnak, többnyire globális erőforrásként, jellemzően több szálon keresztül biztonságosnak kell lennie, vagyis minden egyes allokációt és üzletkötést - általában - szinkronizálni kell a program összes többi halom hozzáférésével. Világos demonstráció: Kép forrása: vikashazrati.wordpress.com | Kazal: A számítógépes RAM-ban tárolják, ugyanúgy, mint a kupacot. A veremben létrehozott változók kiesnek a hatókörből, és automatikusan elosztásra kerülnek. Sokkal gyorsabban allokálható a halom változóihoz képest. Tényleges verem adatstruktúrával valósítva meg. A paraméterek átadásához használt helyi adatokat, visszaküldési címeket tárolja. Túlcsordulhat a verem, ha a verem túl nagy részét használják (többnyire végtelen vagy túl mély rekurzióból, nagyon nagy allokációkból). A veremen létrehozott adatok mutató nélkül használhatók. Akkor használja a verem, ha pontosan tudja, hogy mennyi adatot kell lefoglalnia a fordítási idő előtt, és ez nem túl nagy. Általában a maximális méret már meg van határozva, amikor a program elindul. Halom: A számítógép RAM-ban tárolva, csakúgy, mint a verem. A C ++ - ban a halom változóit manuálisan kell megsemmisíteni, és soha nem esnek ki a hatókörből. Az adatok törléssel, törléssel [] vagy szabaddá tételével szabaddá válnak. Lassabban allokálható a verem változóihoz képest. Igény szerint adatblokk kiosztására szolgál a program számára. Szétaprózódhat, ha sok a kiosztás és az elosztás. C ++ vagy C esetén a kupacon létrehozott adatokra mutatók mutatnak, és új vagy malloc-tal osztják ki őket. Előfordulhat allokációs hiba, ha túl nagy puffert kell kiosztani. Akkor használja a kupacot, ha nem tudja pontosan, mennyi adatra lesz szüksége a futás során, vagy ha sok adatot kell elosztania. Felelős a memória szivárgásáért. Példa: int foo () { char * pBuffer; // <- még nincs kiosztva (kivéve magát a mutatót, amelyet itt osztanak ki a veremben). bool b = igaz; // Kiosztva a veremre. ha (b) { // Hozzon létre 500 bájtot a veremben char puffer [500]; // Hozzon létre 500 bájtot a kupacon pBuffer = új char [500]; } // <- itt puffer van elosztva, a pBuffer nem } // <--- hoppá, van memóriaszivárgás, a törlést [] kellett volna hívnom pBuffer; | A legfontosabb pont az, hogy a halom és a verem általános kifejezéseket tartalmaz a memória lefoglalására. Sokféle módon megvalósíthatók, és a kifejezések az alapfogalmakra vonatkoznak. Egy rakás elemben az elemek egymás tetején helyezkednek el abban a sorrendben, ahogyan azokat ott helyezték el, és csak a felsőt lehet eltávolítani(anélkül, hogy megbuktatnák az egészet). A verem egyszerűsége, hogy nem kell táblázatot vezetnie, amely tartalmazza a lefoglalt memória minden szakaszának rekordját; az egyetlen információ, amire szüksége van, egyetlen mutató a verem végére. A kiosztáshoz és a kiosztás megszüntetéséhez csak növeli és csökkenti az egyetlen mutatót. Megjegyzés: a verem néha megvalósítható úgy, hogy a memória egy részének tetején kezdődjön, és lefelé nyúljon, nem pedig felfelé. Egy kupacban nincs különösebb sorrend a tárgyak elhelyezésének módjáról. Bármilyen sorrendben elérheti és eltávolíthatja az elemeket, mert nincs egyértelmű „felső” elem. A halomallokációhoz teljes nyilvántartást kell vezetni arról, hogy milyen memória van allokálva és mi nem, valamint bizonyos általános karbantartásokat kell végrehajtani a széttöredezettség csökkentése érdekében, a szomszédos memóriaszegmensek megtalálásához elég nagynak ahhoz, hogy elférjenek a kért méretnél, stb. A memória bármikor felosztható, így szabad hely marad. Előfordul, hogy a memória-elosztó karbantartási feladatokat hajt végre, például töredezettségmentesíti a memóriát a lefoglalt memória áthelyezésével vagy a szemétszállítással - futás közben azonosítja, amikor a memória már nem tartozik a területbe, és elosztja azt. Ezeknek a képeknek meglehetősen jó munkát kell elvégezniük, amikor leírják a memória felosztásának és felszabadításának két módját egy rakatban és egy kupacban. Yum! Mennyire ellenőrzi őket az operációs rendszer vagy a nyelv futásideje? Mint már említettük, a halom és a verem általános kifejezés, és sokféleképpen megvalósítható. A számítógépes programokban általában van egy hívásveremnek nevezett verem, amely tárolja az aktuális függvény szempontjából releváns információkat, például egy mutatót annak a függvénynek, ahonnan hívták, és minden helyi változót. Mivel a függvények más funkciókat hívnak meg, majd visszatérnek, a verem növekszik és zsugorodik, hogy információkat tároljon a függvényekről a hívásveremben. Egy program valójában nem futtathatja felettük futásidőt; a programozási nyelv, az operációs rendszer és még a rendszer architektúrája is meghatározza. A halom egy általános kifejezés, amelyet bármilyen dinamikusan és véletlenszerűen lefoglalt memória számára használnak. azaz rendezetlen. A memóriát általában az operációs rendszer osztja ki, az alkalmazás az API-függvényeket hívja meg a felosztás elvégzéséhez. A dinamikusan lefoglalt memória kezeléséhez meglehetősen kevés rezsi szükséges, amelyet általában a használt programozási nyelv vagy környezet futásidejű kódja kezel. Mi a hatályuk? A hívásverem olyan alacsony szintű fogalom, hogy a programozás szempontjából nem kapcsolódik a „hatókörhöz”. Ha szétszerel egy kódot, akkor viszonylagos mutatóstílus-hivatkozások jelennek meg a verem egyes részein, de ami a magasabb szintű nyelvet illeti, a nyelv a saját alkalmazási szabályait írja elő. A verem egyik fontos szempontja, hogy amint egy függvény visszatér, a függvény minden helyi része azonnal felszabadul a veremből. Ez úgy működik, ahogyan azt a programozási nyelvek működésétől elvárható. Halomban azt is nehéz meghatározni. A hatókör bármi, amit az operációs rendszer kitesz, de a programozási nyelv valószínűleg hozzáadja annak szabályait, hogy mi az a "hatókör" az alkalmazásában. A processzor architektúrája és az operációs rendszer virtuális címzést használ, amelyet a processzor fizikai címekre fordít, és vannak oldalhibák stb. Nyomon követik, hogy melyik oldal mely alkalmazásokhoz tartozik. Soha nem kell aggódnod emiatt, mert csak azt a módszert használod, amelyet a programozási nyelv használ a memória lefoglalására és felszabadítására, valamint ellenőrzi a hibákat (ha a kiosztás / felszabadítás bármilyen okból nem sikerül). Mi határozza meg mindegyik méretét? Ez megint a nyelvtől, a fordítótól, az operációs rendszertől és az architektúrától függ. A verem általában előre van kiosztva, mert definíció szerint összefüggő memóriának kell lennie. A nyelv fordítója vagy az operációs rendszer határozza meg a méretét. Nem tárol hatalmas mennyiségű adatot a veremben, így elég nagy lesz ahhoz, hogy soha ne használja fel teljesen, kivéve a nem kívánt végtelen rekurziót (ezért "verem túlcsordulás") vagy más szokatlan programozási döntéseket. A halom általános kifejezés mindenre, amit dinamikusan el lehet osztani. Attól függően, hogy nézi, folyamatosan változik a mérete. A modern processzorokban és operációs rendszerekben a pontos működési mód egyébként is nagyon elvonatkoztatott, ezért általában nem kell sokat aggódnod a működése miatt mélyen, kivéve, hogy (olyan nyelveken, ahol ez lehetővé teszi) nem szabad olyan memóriát használni még nem osztott ki vagy memóriát, amelyet felszabadított. Mi teszi az embert gyorsabbá? A verem gyorsabb, mert minden szabad memória mindig összefüggő. A szabad memória összes szegmensét nem kell felsorolni, csak egyetlen mutatót kell a verem aktuális tetejére vezetni. A fordítók ezt a mutatót általában egy speciális, gyors nyilvántartásban tárolják erre a célra. Sőt, a verem későbbi műveletei általában a memória nagyon közeli területeire koncentrálódnak, ami nagyon alacsony szinten jó az optimalizálásra a processzor általgyorsítótárak. | (Ezt a választ áthelyeztem egy másik kérdésre, amely többé-kevésbé ennek a kérdésnek a dupija volt.) A kérdésedre adott válasz a megvalósításra vonatkozik, és a fordítókban és a processzor architektúrákban változhat. Itt van azonban egy egyszerűsített magyarázat. A verem és a halom is az alapul szolgáló operációs rendszer által lefoglalt memóriaterületek (gyakran virtuális memória, amelyet igény szerint leképeznek a fizikai memóriára). Többszálas környezetben mindegyik szálnak megvan a maga teljesen független vereme, de megosztják a kupacot. Az egyidejű hozzáférést a kupacon kell ellenőrizni, és ez nem lehetséges a veremben. A kupac A kupac a használt és szabad blokkok összekapcsolt listáját tartalmazza. A halom új allokációi (új vagy malloc által) kielégülnek, ha az egyik szabad blokkból létrehozunk egy megfelelő blokkot. Ehhez frissíteni kell a halom blokkjainak listáját. Ez a meta információ a kupac blokkjairól a kupacon is tárolódik, gyakran egy kis területen, közvetlenül minden blokk előtt. A halom növekedésével az új blokkokat gyakran az alacsonyabb címek és a magasabb címek között osztják ki. Tehát úgy gondolhat a halomra, mint egy memóriablokk-halomra, amely a memória kiosztásakor növekszik. Ha a halom túl kicsi a kiosztáshoz, a méret gyakran növelhető, ha több memóriát szerez az alapul szolgáló operációs rendszerből. Sok kicsi blokk kiosztása és elosztása elhagyhatja a kupacot olyan állapotban, ahol sok kicsi szabad blokk van tarkítva a használt blokkok között. Egy nagy blokk kiosztására irányuló kérelem kudarcot vallhat, mert egyik szabad blokk sem elég nagy ahhoz, hogy kielégítse az allokációs kérelmet, annak ellenére, hogy az ingyenes blokkok együttes mérete elég nagy lehet. Ezt halomtöredezettségnek nevezzük. Ha egy szabad blokkkal szomszédos használt blokkot osztanak le, akkor az új szabad blokkot össze lehet egyesíteni a szomszédos szabad blokkkal egy nagyobb szabad blokk létrehozása érdekében, amely hatékonyan csökkenti a kupac töredezettségét. A verem A verem gyakran szoros együttműködésben működik a CPU speciális regisztrációjával, a verem mutatójának. Kezdetben a veremmutató a verem tetejére mutat (a verem legmagasabb címe). A CPU speciális utasításokat tartalmaz az értékek verembe tolására és a veremből való visszapattanására. Minden löket eltárolja az értéket a veremmutató aktuális helyén, és csökkenti a veremmutatót. A pop lekéri a veremmutató által mutatott értéket, majd növeli a veremmutatót (ne tévesszen meg az a tény, hogy egy érték hozzáadásakor a veremhez csökken a verem mutatója, és egy érték eltávolításával megnő. Ne feledje, hogy a verem az alsó). A tárolt és lekért értékek a CPU regiszterek értékei. Amikor egy funkciót hívnak, a CPU speciális utasításokat használ, amelyek megnyomják az aktuális utasításmutatót, vagyis a veremen végrehajtó kód címét. Ezután a CPU a funkció beállításához ugrik a utasításmutató a meghívott függvény címére. Később, amikor a függvény visszatér, a régi utasításmutató kiugrik a veremből, és a végrehajtás a kódnál folytatódik, közvetlenül a függvény meghívása után. Funkció megadásakor a verem mutatója csökken, hogy több hely legyen a veremben a helyi (automatikus) változók számára. Ha a függvénynek van egy helyi 32 bites változója, akkor négy bájtot tesznek félre a veremre. Amikor a függvény visszatér, a veremmutatót visszahelyezik a kiosztott terület felszabadításához. Ha egy funkciónak vannak paraméterei, ezeket a függvény hívása előtt a veremre tolják. A függvény kódja ekkor képes az aktuális verem mutatóból felfelé navigálni az értékek megtalálásához. A fészkelési funkció hívásai varázslatként működnek. Minden új hívás kiosztja a funkcióparamétereket, a visszatérési címet és a helyet a helyi változók számára, és ezek az aktiválási rekordok egymásra helyezhetők beágyazott hívásokhoz, és a megfelelő módon kikapcsolódnak, amikor a funkciók visszatérnek. Mivel a verem korlátozott memóriablokk, túl sok beágyazott függvény meghívásával és / vagy a helyi változók számára túl sok hely lefoglalásával okozhatja a verem túlcsordulását. Gyakran a veremhez használt memóriaterület úgy van beállítva, hogy a verem alja (legalacsonyabb cím) alá írva csapdát vagy kivételt vált ki a CPU-ban. Ezt a kivételes feltételt aztán a futás lefoglalhatja, és valamilyen verem túlcsordulási kivételsé alakíthatja. Kiosztható-e egy függvény a kupacban verem helyett? Nem, a függvények aktiválási rekordjai (azaz helyi vagy automatikus változók) vannak kiosztva a veremben, amelyet nemcsak ezeknek a változóknak a tárolására használnak, hanem a beágyazott függvényhívások nyomon követésére is. A halom kezelésének módja valóban a futási környezetben múlik. A C mallocot, a C ++ pedig az újat használja, de sok más nyelv rendelkezik szemétgyűjtéssel. A verem azonban alacsonyabb szintű szolgáltatás, amely szorosan kapcsolódik a processzor architektúrájához. A halom növesztése, ha nincs elég hely, azóta sem túl nehézmegvalósítható a halmot kezelő könyvtárhívásban. A verem növelése azonban gyakran lehetetlen, mivel a verem túlcsordulása csak akkor derül ki, ha már túl késő; és a végrehajtási szál leállítása az egyetlen megvalósítható lehetőség. | A következő C # kódban public void 1. módszer () { int i = 4; int y = 2; class1 cls1 = new class1 (); } Így kezelhető a memória Helyi változók, amelyeknek csak addig kell tartaniuk, amíg a függvényhívás a veremben van. A kupacot olyan változókhoz használják, amelyek élettartamát nem igazán ismerjük előre, de elvárjuk, hogy egy ideig tartsanak. A legtöbb nyelvben kritikus fontosságú, hogy fordításkor tudjuk, mekkora egy változó, ha a veremben akarjuk tárolni. Az objektumok (amelyek méretükben változnak, ahogy frissítjük őket) halmozódnak, mert a létrehozáskor nem tudjuk, meddig fognak tartani. Sok nyelven a kupac szemét gyűjt, hogy olyan objektumokat (például cls1 objektumot) találjon, amelyekre már nincs hivatkozás. A Java-ban a legtöbb objektum közvetlenül a kupacba kerül. Az olyan nyelveken, mint a C / C ++, a struktúrák és az osztályok gyakran a veremben maradhatnak, ha éppen nem mutatóval foglalkozol. További információ itt található: A verem és a halom memória-allokáció közötti különbség «timmurphy.org és itt: Objektumok létrehozása a verem és a kupacon Ez a cikk a fenti kép forrása: Hat fontos .NET-fogalom: Verem, halom, értéktípusok, hivatkozási típusok, ökölvívás és kibontás - CodeProject de vegye figyelembe, hogy tartalmazhat néhány pontatlanságot. | A verem Ha meghív egy függvényt, akkor a függvény argumentumai, valamint néhány egyéb rezsi kerül a verembe. Néhány információ (például a visszautazás helye) szintén ott tárolódik. Ha deklarál egy változót a függvényen belül, akkor ez a változó is el lesz rendelve a veremben. A verem elosztása meglehetősen egyszerű, mert mindig a fordított sorrendben osztogat, amelyben kiosztja. Verem cuccok hozzáadódnak, amikor belép a funkciókba, a megfelelő adatok eltávolításra kerülnek, amikor kilép belőlük. Ez azt jelenti, hogy hajlamos a verem egy kis régiójában maradni, hacsak nem hív meg sok olyan függvényt, amely sok más funkciót hív meg (vagy nem hoz létre rekurzív megoldást). A Halom A kupac egy általános név arra, hogy hova tegye a létrehozott adatokat menet közben. Ha nem tudja, hogy a program hány űrhajót fog létrehozni, akkor valószínűleg az új (vagy malloc vagy azzal egyenértékű) operátort használja az egyes űrhajók létrehozásához. Ez az allokáció egy darabig fennmarad, így valószínűleg más sorrendben szabadítunk fel dolgokat, mint amit mi hoztunk létre. Így a halom sokkal összetettebb, mert végül olyan memóriaterületek vannak, amelyek kihasználatlanul vannak összevonva olyan darabokkal, amelyek - az emlékezet széttöredezik. A szükséges méretű szabad memória megtalálása nehéz feladat. Ezért kerülni kell a kupacot (bár még mindig gyakran használják). Végrehajtás A verem és a kupac megvalósítása általában a futásidejű / operációs rendszerig tart. Gyakran a teljesítménykritikus játékok és egyéb alkalmazások létrehozzák a saját memóriamegoldásaikat, amelyek egy nagy memóriaterületet ragadnak ki a kupacból, majd belsőleg elosztják, hogy ne támaszkodjanak az operációs rendszerre. Ez csak akkor praktikus, ha a memóriahasználata teljesen eltér a megszokottól - azaz olyan játékok esetében, ahol egy szintet tölt be egy hatalmas művelet során, és egy másik hatalmas művelet során el tudja rabolni az egészet. Fizikai hely a memóriában Ez kevésbé releváns, mint gondolná a Virtuális memória nevű technológia miatt, amely arra készteti a programot, hogy hozzáférjen egy bizonyos címhez, ahol a fizikai adatok valahol máshol vannak (még a merevlemezen is!). A veremhez kapott címek növekvő sorrendben vannak, ahogy a hívásfa mélyül. A halom címei kiszámíthatatlanok (vagyis a megvalósítás specifikusak), és őszintén szólva nem fontosak. | Tisztázandó, hogy ennek a válasznak helytelen információi vannak (thomas kijavította válaszát megjegyzések után, jó :)). Más válaszok csak elkerülik annak magyarázatát, hogy mit jelent a statikus allokáció. Tehát elmagyarázom az allokáció három fő formáját és azt, hogy ezek hogyan viszonyulnak általában az alábbi kupachoz, veremhez és adatszegmenshez. Néhány példát bemutatok mind a C / C ++, mind a Python nyelven, hogy segítsek az embereknek megérteni. A "statikus" (AKA statikusan kiosztott) változók nincsenek kiosztva a veremben. Ne feltételezzük - sokan csak azért teszik, mert a "statikus" hangzás nagyon hasonlít a "verem" szóhoz. Valójában sem a veremben, sem a kupacban nem léteznek. Ezek az úgynevezett adatszegmens részei. Általában azonban jobb, ha a "verem" és a "halom" helyett a "hatókört" és az "élettartamot" vesszük figyelembe. A hatókör arra utal, hogy a kód mely részei férhetnek hozzá egy változóhoz. Általában a lokális hatókörre gondolunk (csak az aktuális funkcióval érhető el), szemben a globális hatókörrel (bárhonnan elérhető), bár a hatókör sokkal bonyolultabbá válhat. Az élettartam arra vonatkozik, amikor egy változó kiosztásra kerül és elosztásra kerül a program végrehajtása során. Általában a statikus allokációra gondolunk (változóa program teljes időtartama alatt megmarad, ami hasznos lehet ugyanazok az információk tárolásához több függvényhívás során), szemben az automatikus kiosztással (a változó csak egy funkcióhoz való egyetlen hívás során áll fenn, így csak az Ön függvény, és ha elkészült, eldobható) szemben a dinamikus allokációval (olyan változók, amelyek időtartamát futás közben határozzák meg, a fordítási idő helyett, például statikus vagy automatikus). Bár a legtöbb fordító és tolmács hasonlóan hajtja végre ezt a viselkedést a halmok, halmok stb. Használatában, a fordító néha megszegheti ezeket a konvenciókat, ha akar, amíg a viselkedés helyes. Például az optimalizálás miatt egy helyi változó csak egy regiszterben létezhet, vagy teljesen eltávolítható, annak ellenére, hogy a legtöbb helyi változó a veremben létezik. Amint néhány megjegyzésben rámutattak, szabadon alkalmazhat olyan fordítót, amely nem is használ verem vagy kupacot, hanem más tároló mechanizmusokat (ritkán, mivel a verem és a halom nagyszerű ehhez). Néhány illusztrált egyszerű kóddal ellátott C kódot adok meg. A tanulás legjobb módja egy program futtatása egy hibakereső alatt, és a viselkedés figyelése. Ha inkább python-t olvas, ugorjon a válasz végére :) // Statikusan kiosztva az adatszegmensben a program / DLL első betöltésekor // Deallocated, amikor a program / DLL kilép // hatókör - a kód bármely pontjáról elérhető int someGlobalVariable; // Statikusan kiosztva az adatszegmensben a program első betöltésekor // Deallocated, amikor a program / DLL kilép // hatókör - bárhonnan elérhető az adott kódfájlban static int someStaticVariable; // A "someArgument" a MyFunction minden egyes meghívásakor kiosztásra kerül a veremben // A "someArgument" elosztása megtörténik, amikor a MyFunction visszatér // hatókör - csak a MyFunction () oldalon érhető el void MyFunction (int someArgument) { // Statikusan kiosztva az adatszegmensben a program első betöltésekor // Deallocated, amikor a program / DLL kilép // hatókör - csak a MyFunction () oldalon érhető el static int someLocalStaticVariable; // A veremre allokálva, minden alkalommal, amikor a MyFunction meghívásra kerül // Deallocated, amikor a MyFunction visszatér // hatókör - csak a MyFunction () oldalon érhető el int someLocalVariable; // A MyFunction minden egyes meghívásakor egy * mutató * kerül kiosztásra a veremben // Ezt a mutatót akkor osztjuk le, amikor a MyFunction visszatér // hatókör - a mutató csak a MyFunction () oldalon érhető el int * someDynamicVariable; // Ez a sor teret enged egy egész szám kiosztásához a kupacban // amikor ezt a sort végrehajtják. Ne feledje, hogy ez nem a. Elején található // a MyFunction () hívása, mint az automatikus változók // hatókör - csak a MyFunction () kódja érheti el ezt a helyet // * ezen a bizonyos változón keresztül *. // Ha azonban másutt adja át a címet, akkor azt a kódot // hozzá is férhet someDynamicVariable = új int; // Ez a sor elosztja a halom egész számának helyét. // Ha nem írtuk meg, akkor az emlékezet "kiszivárog". // Vegye figyelembe a verem és a halom közötti alapvető különbséget // a kupacot kezelni kell. A verem nekünk van kezelve. delete someDynamicVariable; // Más esetekben ahelyett, hogy elosztaná ezt a kupacteret, ön // lehet, hogy a címet egy állandóbb helyre tárolja, hogy később felhasználhassa. // Néhány nyelv még az ügyfelek ellátásáról is gondoskodik számodra ... de // mindig futás közben kell gondoskodni valamilyen mechanizmusról. // Amikor a függvény visszatér, someArgument, someLocalVariable // és a someDynamicVariable mutató el van osztva. // A someDynamicVariable által mutatott tér már volt // visszaszolgáltatás előtt lefoglalták. Visszatérés; } // Ne feledje, hogy someGlobalVariable, someStaticVariable és // someLocalStaticVariable továbbra is léteznek, és nem // addig foglalkozunk, amíg a program ki nem lép. Különösen megrendítő példa arra, miért fontos különbséget tenni az élettartam és a hatókör között, hogy egy változó helyi hatókörű, de statikus élettartamú lehet - például a fenti kódmintában a "someLocalStaticVariable". Az ilyen változók nagyon zavarossá tehetik közös, de informális névadási szokásainkat. Például, amikor azt mondjuk, hogy "helyi", akkor általában "lokálisan lefedett, automatikusan lefoglalt változót" értünk, globálisnak pedig azt, hogy "globálisan ható statikusan lefoglalt változót". Sajnos, amikor olyan dolgokról van szó, mint a "statikusan lefoglalt változók fájlköre", sokan csak azt mondják, hogy "huh ???". A C / C ++ szintaxis-választásai közül néhány súlyosbítja ezt a problémát - például sokan úgy gondolják, hogy a globális változók az alább látható szintaxis miatt nem "statikusak". int var1; // Globális hatóköre és statikus allokációja van statikus int var2; // fájlköre és statikus allokációja van int main () {return 0;} Ne feledje, hogy a "statikus" kulcsszó beírása a fenti deklarációba megakadályozza, hogy a var2 globális hatályú legyen. Ennek ellenére a globális var1 statikus allokációval rendelkezik. Ez nemintuitív! Emiatt megpróbálom soha nem használni a "statikus" szót a hatókör leírásakor, ehelyett valami olyat mondok, mint "fájl" vagy "fájl korlátozott". Sokan azonban a "statikus" vagy a "statikus hatókör" kifejezéssel olyan változót írnak le, amelyhez csak egy kódfájlból lehet hozzáférni. Az élettartam összefüggésében a "statikus" mindig azt jelenti, hogy a változó a program indításakor kiosztásra kerül, és a program kilépésekor elosztásra kerül. Vannak, akik ezeket a fogalmakat C / C ++ specifikusnak tartják. Ezek nem. Például az alábbi Python-minta az allokáció mindhárom típusát szemlélteti (az értelmezett nyelvekben vannak olyan finom különbségek, amelyekre itt nem térek ki). a datetime importálásból datetime osztályú állat: _FavoriteFood = 'Undefined' # A _FavoriteFood statikusan van lefoglalva def PetAnimal (self): curTime = datetime.time (datetime.now ()) # curTime automatikusan kiosztásra kerül print ("Köszönöm, hogy simogattál. De ez a" + str (curTime) + ", etetned kell. A kedvenc ételem a" + self._FavoriteFood) osztályú macska (állat): _FavoriteFood = 'tonhal' # Megjegyzés: mivel felülbíráljuk, a Cat osztálynak megvan a maga statikusan kiosztott _FavoriteFood változója, amely különbözik az Animalétől osztályú kutya (állat): _FavoriteFood = 'steak' # Hasonlóképpen, a Dog osztály is megkapja a saját statikus változóját. Fontos megjegyezni - ezt az egy statikus változót megosztják a Dog összes példánya, ezért nem dinamikus! ha __name__ == "__main__": bajusz = Cat () # Dinamikusan elosztva fido = Kutya () # Dinamikusan elosztva rinTinTin = Kutya () # Dinamikusan kiosztva pofaszakáll.PetAnimal () fido.PetAnimal () rinTinTin.PetAnimal () Dog._FavoriteFood = 'tejcsontok' pofaszakáll.PetAnimal () fido.PetAnimal () rinTinTin.PetAnimal () # Kimenet: # Köszönöm, hogy megsimogattál. De ez 13: 05: 02.255000, etetni kellene. Kedvenc ételem a tonhal # Köszönöm, hogy megsimogattál. De 13: 05: 02.255000 van, etetned kellene. Kedvenc ételem a steak # Köszönöm, hogy megsimogattál. De ez 13: 05: 02.255000, etetni kellene. Kedvenc ételem a steak # Köszönöm, hogy megsimogattál. De ez 13: 05: 02.255000, etetni kellene. Kedvenc ételem a tonhal # Köszönöm, hogy megsimogattál. De 13: 05: 02.255000 van, etetned kellene. Kedvenc ételem a tejcsont # Köszönöm, hogy megsimogattál. De ez 13: 05: 02.256000, etetni kellene. Kedvenc ételem a tejcsont | Mások elég jól megválaszolták a nagy vonásokat, ezért néhány részletet bemutatok. A veremnek és a kupacnak nem kell egyedinek lennie. Gyakori helyzet, amikor egynél több verem van, ha egy folyamatban több szál is van. Ebben az esetben minden szálnak megvan a maga vereme. Több halom is lehet, például egyes DLL-konfigurációk különböző DLL-ek kiosztását eredményezhetik különböző halmokból, ezért általában rossz ötlet egy másik könyvtár által lefoglalt memória felszabadítása. A C-ben a változó hosszúságú allokáció előnyeit élvezheti az allok használatával, amely a veremben allokál, szemben az allokációval, amely a kupacon allokál. Ez a memória nem fogja túlélni a visszatérési utasítást, de hasznos egy semmiből való pufferhez. Hatalmas ideiglenes puffer készítése a Windows rendszeren, amelyből nem sokat használ, nem ingyenes. Ez azért van, mert a fordító létrehoz egy verem-szondahurkot, amelyet minden alkalommal meghívnak, amikor a funkciót megadják, hogy megbizonyosodjon arról, hogy a verem létezik (mivel a Windows a verem végén egyetlen őrző oldalt használ arra, hogy észlelje, mikor kell a verem növekednie. Ha egynél több oldalra fér hozzá a memóriához a verem végéből, összeomlik). Példa: érvényteleníti a működésemet () { char char [10000000]; // Tegyen valamit, ami csak az idő nagy 99% -ának első 1K-jához használ fel. } | Mások közvetlenül válaszoltak a kérdésedre, de amikor megpróbálom megérteni a verem és a kupacot, hasznosnak tartom figyelembe venni egy hagyományos UNIX folyamat memóriarendezését (szálak és mmap () alapú allokátorok nélkül). A Memóriakezelés szószedete weblapon látható ez a memóriaelrendezés. A verem és a kupac hagyományosan a folyamat virtuális címterének ellentétes végén helyezkedik el. A verem elérésekor automatikusan növekszik, a kernel által beállított méretig (amelyet a setrlimit (RLIMIT_STACK, ...) segítségével állíthatunk be). A halom növekszik, amikor a memóriafoglaló meghívja a brk () vagy az sbrk () rendszerhívást, és több fizikai memória oldalt képez a folyamat virtuális címterébe. A virtuális memória nélküli rendszerekben, például néhány beágyazott rendszerben, gyakran ugyanaz az alapelrendezés érvényes, kivéve, ha a verem és a halom fix méretű. Más beágyazott rendszerekben (például a Microchip PIC mikrovezérlőkön alapuló rendszerekben) azonban a programverem egy külön memóriablokk, amelyet az adatmozgatási utasítások nem címezhetnek meg, és csak programfolyamat-utasításokkal (hívás, visszatérés stb.). Más architektúrák, például az Intel Itanium processzorok, több halmot tartalmaznak. Ebben az értelemben a verem a CPU architektúrájának eleme. | A verem egy részmemória, amely több kulcsszerelési nyelvi utasítással kezelhető, például a „pop” (egy érték eltávolítása és visszaküldése a veremből) és a „push” (egy érték tolása a verembe), de hívás is (alprogram meghívása - ez megnyomja a címet, hogy visszatérjen a verembe) és visszatér (visszatér egy alprogramból - ez eldobja a címet a veremből és ugrik hozzá). Ez a verem mutató regiszter alatti memóriaterülete, amelyet szükség szerint beállíthatunk. A verem arra is használható, hogy argumentumokat továbbítson szubrutinok számára, valamint az értékek megőrzésére a regiszterekben, mielőtt alprogramokat hívna. A kupac a memória egy része, amelyet az operációs rendszer ad egy alkalmazásnak, általában egy syscallon keresztül, mint például a malloc. A modern operációs rendszereken ez a memória olyan oldalak összessége, amelyekhez csak a hívási folyamat fér hozzá. A verem méretét futás közben határozzák meg, és általában nem növekszik a program indítása után. Egy C programban a veremnek elég nagynak kell lennie ahhoz, hogy minden függvényben deklarált változót megtarthasson. A halom szükség szerint dinamikusan növekszik, de az operációs rendszer végül hívást kezdeményez (gyakran növeli a kupacot a malloc által kért értéknél nagyobb mértékben, így legalább néhány jövőbeli mallocsnak nem kell visszatérnie a kernelhez több memóriát kap. Ez a viselkedés gyakran testreszabható) Mivel a program elindítása előtt kiosztotta a köteget, soha nem kell mallocskáznia a verem használatának megkezdése előtt, így ez egy kis előny. A gyakorlatban nagyon nehéz megjósolni, hogy mi lesz gyors és mi lassú a modern operációs rendszerekben, amelyek virtuális memória alrendszerrel rendelkeznek, mert az oldalak megvalósításának és tárolásának részlete a megvalósítás részlete. | Mi az a verem? A verem egy halom tárgy, általában egy szépen elrendezett tárgy. A számítástechnikai architektúrák veremei azok a memóriaterületek, ahová az adatokat utoljára az elsőben adják ki vagy távolítják el. Többszálas alkalmazásban minden szálnak meg lesz a maga vereme. Mi az a kupac? A kupac rendezetlen gyűjteménye a véletlenszerűen halmozott dolgoknak. A számítási architektúrákban a kupac egy dinamikusan lefoglalt memória területe, amelyet az operációs rendszer vagy a memóriakezelő könyvtár automatikusan kezel. A halom memóriáját rendszeresen lefoglalják, elosztják és átméretezik a program futtatása során, ez pedig a töredezettségnek nevezett problémához vezethet. Töredezettség akkor fordul elő, ha a memóriaobjektumok között kicsi terek vannak, amelyek túl kicsiek ahhoz, hogy további memóriaobjektumokat tároljanak. A nettó eredmény a kupac tér százalékos aránya, amely nem használható további memóriafoglalásokhoz. Mindketten együtt Többszálas alkalmazásban minden szálnak meg lesz a maga vereme. De a különböző szálak megosztják a kupacot. Mivel a különböző szálak több szálon futó alkalmazásban osztják meg a kupacot, ez azt is jelenti, hogy bizonyos szálak közötti koordinációnak kell lennie, hogy ne próbálják meg elérni és kezelni a kupacban lévő ugyanazokat a memóriadarabokat. ugyanakkor. Melyik gyorsabb - a verem vagy a kupac? És miért? A verem sokkal gyorsabb, mint a kupac. Ennek oka a memória lefoglalása a veremben. A memória lefoglalása a veremben olyan egyszerű, mint a veremmutató felfelé mozgatása. Azok számára, akik még nem ismerik a programozást, valószínűleg érdemes használni a verem, mivel ez könnyebb. Mivel a verem kicsi, akkor érdemes használni, ha pontosan tudja, mennyi memóriára lesz szüksége az adataihoz, vagy ha tudja, hogy az adat mérete nagyon kicsi. Jobb használni a kupacot, ha tudja, hogy sok memóriára lesz szüksége az adataihoz, vagy csak nem biztos benne, hogy mennyi memóriára lesz szüksége (például dinamikus tömb esetén). Java memória modell A verem az a memóriaterület, ahol a helyi változókat (a módszer paramétereit is beleértve) tárolják. Ha objektumváltozókról van szó, ezek csupán hivatkozások (mutatók) a halom tényleges tárgyaira. Valahányszor egy objektumot példányosítanak, egy halom memóriát tárolnak az objektum adatainak (állapotának) tárolására. Mivel az objektumok más objektumokat is tartalmazhatnak, ezeknek az adatoknak egy része valójában hivatkozhat ezekre a beágyazott objektumokra. | Azt hiszem, sok más ember adott többnyire helyes válaszokat ebben a kérdésben. Az egyik részlet, ami kimaradt, az az, hogy a "kupacot" valójában valószínűleg "ingyen boltnak" kellene nevezni. Ennek a megkülönböztetésnek az az oka, hogy az eredeti ingyenes áruház "binomiális halom" néven ismert adatstruktúrával valósult meg. Ezért a malloc () / free () korai megvalósításaiból történő felosztás halomból történt. Azonban ebben a modern napban a legtöbb ingyenes áruház nagyon bonyolult adatstruktúrákkal valósul meg, amelyek nem binomiális halmok. | Érdekes dolgokat tehet a verem mellett. Például vannak olyan funkciói, mint az allok (feltételezve, hogy túl tudnak lépni a használatával kapcsolatos rengeteg figyelmeztetésen), amely a malloc egyik formája, amelykifejezetten a halmot használja, nem pedig a kupacot a memóriához. Ennek ellenére a veremalapú memóriahibák a legrosszabbak, amelyeket tapasztaltam. Ha halom memóriát használ, és túllépi a lefoglalt blokk határait, akkor megfelelő esélye van egy szegmenshiba kiváltására. (Nem 100%: a blokk véletlenül szomszédos lehet egy mással, amelyet korábban kiosztott.) De mivel a veremben létrehozott változók mindig szomszédosak egymással, a határokon kívüli írás megváltoztathatja egy másik változó értékét. Megtudtam, hogy valahányszor úgy érzem, hogy a programom nem engedelmeskedik a logika törvényeinek, valószínűleg puffertúlcsordulás. | Egyszerűen a veremben jönnek létre a helyi változók. Ezenkívül minden egyes szubrutin meghívásakor a programszámláló (mutató a következő gépi utasításra) és minden fontos regiszter, és néha a paraméterek a veremre tolódnak. Ezután a szubrutinon belüli minden helyi változót a veremre tolják (és onnan használják). Amikor az alprogram befejeződik, ez a cucc visszazökken a veremből. A PC és a regisztrációs adatok megkapják és visszateszik a helyre, ahol megjelentek, így a program folytathatja vidám útját. A halom a memória dinamikus memória-allokációinak területe (kifejezett "új" vagy "allokáló" hívások). Ez egy speciális adatstruktúra, amely képes nyomon követni a különböző méretű memóriablokkokat és azok kiosztási állapotát. A "klasszikus" rendszerekben a RAM-ot úgy telepítették, hogy a verem mutató a memória alján, a kupac mutató a tetején kezdődött, és egymás felé nőttek. Ha átfedik egymást, akkor kifogy a RAM. Ez azonban nem működik a modern, többszálas operációs rendszerekkel. Minden szálnak rendelkeznie kell saját veremmel, és ezek dinamikusan létrehozhatók. | A WikiAnwser oldalról. Kazal Amikor egy függvény vagy egy módszer meghív egy másik függvényt, amely felváltva hív egy másik függvényt stb., Az összes függvény végrehajtása felfüggesztve marad, amíg a legutolsó függvény visszaadja az értékét. Ez a függesztett függvényhívások láncolata a verem, mert a verem elemei (függvényhívások) függnek egymástól. A verem fontos figyelembe venni a kivételkezelésnél és a szál végrehajtásakor. Halom A halom egyszerűen a memória, amelyet a programok a változók tárolására használnak. A halom elemének (változók) nincs függősége egymással, és bármikor véletlenszerűen elérhető. | Kazal Nagyon gyors hozzáférés Ne kelljen kifejezetten elosztani a változókat A helyet a CPU hatékonyan kezeli, a memória nem válik széttöredezetté Csak lokális változók A verem méretének korlátozása (operációs rendszertől függ) A változók átméretezése nem lehetséges Halom A változók globálisan érhetők el Nincs korlátozás a memória méretére vonatkozóan (Viszonylag) lassabb hozzáférés Nincs garantáltan hatékony helykihasználás, a memória idővel széttöredezhet, amikor a memória blokkokat felosztják, majd felszabadítják Kezelnie kell a memóriát (a változók kiosztása és felszabadítása a felelős) A változók átméretezhetők a realloc () használatával | Röviden A verem a statikus memória-elosztáshoz és a halom a dinamikus memória-allokációhoz használható, mindkettő a számítógép RAM-jában van tárolva. Részletesen A verem A verem egy "LIFO" (utoljára be, először ki) adatstruktúra, amelyet a CPU meglehetősen szorosan kezel és optimalizál. Valahányszor egy függvény új változót deklarál, azt a veremre "tolják". Aztán ahányszor egy függvény kilép, az összes függvény felszabadul (vagyis törlésre kerül). Miután a veremváltozó felszabadult, az a memóriaterület elérhetővé válik más veremváltozók számára. A verem változók tárolására való használatának előnye, hogy a memória kezelése az Ön számára történik. Nem kell kézzel lefoglalnia a memóriát, vagy szabadítania, ha már nincs rá szüksége. Sőt, mivel a CPU olyan hatékonyan szervezi a verem memóriáját, a verem változókból való olvasás és azok írása nagyon gyors. További információk itt találhatók. A Halom A kupac a számítógép memóriájának olyan része, amelyet az Ön számára nem kezelnek automatikusan, és amelyet a CPU nem kezel olyan szorosan. Ez egy szabadabban lebegő memóriaterület (és nagyobb). A halom memóriájának lefoglalásához a malloc () vagy a calloc () függvényeket kell használnia, amelyek beépített C funkciók. Miután kiosztott memóriát a kupacban, Ön felelős az ingyenes () használatával a memória elosztására, ha már nincs rá szüksége. Ha ezt elmulasztja, akkor a programja úgynevezett memóriaszivárgással rendelkezik. Vagyis a halom memóriáját továbbra is félretesszük (és más folyamatok számára nem lesz elérhető). Amint a hibakeresés szakaszban látni fogjuk, van egy Valgrind nevű eszköz, amely segíthet a memóriaszivárgások észlelésében. A veremtől eltérően a halomnak nincsenek korlátozásai a változó méretre (eltekintve a számítógép nyilvánvaló fizikai korlátaitól). A halom memória kissé lassabban olvasható ki és írható oda, mert a halom memóriájának eléréséhez mutatókat kell használnia. Rövidesen beszélünk a mutatókról. A veremtől eltérőena kupacon létrehozott változók minden funkcióval elérhetők, bárhol a programban. A halomváltozók lényegében globálisak. További információk itt találhatók. A veremben kiosztott változókat közvetlenül a memóriába tárolják, és ehhez a memóriához nagyon gyorsan hozzáférnek, és a program lefordításával a program lefordításakor foglalkozik. Amikor egy függvény vagy egy módszer meghív egy másik függvényt, amely felváltva hív egy másik függvényt stb., Az összes függvény végrehajtása felfüggesztve marad, amíg a legutolsó függvény visszaadja az értékét. A verem mindig LIFO sorrendben van lefoglalva, a legutóbb lefoglalt blokk mindig a következő felszabadítandó blokk. Ez igazán egyszerűvé teszi a verem nyomon követését, egy blokk felszabadítása a veremből nem más, mint egy mutató beállítása. A halomon allokált változók memóriája futási időben van lefoglalva, és ehhez a memóriához való hozzáférés kissé lassabb, de a kupac méretét csak a virtuális memória mérete korlátozza. A halom elemei nem függenek egymástól, és bármikor véletlenszerűen elérhetők. Bármikor kioszthat egy blokkot, és bármikor felszabadíthatja. Ez sokkal bonyolultabbá teszi annak nyomon követését, hogy a halom mely részei vannak kiosztva vagy szabadon bármikor. Használhatja a veremet, ha pontosan tudja, hogy mennyi adatot kell lefoglalnia a fordítási idő előtt, és ez nem túl nagy. Használhatja a kupacot, ha nem tudja pontosan, mennyi adatra lesz szüksége futás közben, vagy ha sok adatot kell lefoglalnia. Többszálas helyzetben mindegyik szálnak megvan a maga teljesen független vereme, de megosztják a kupacot. A verem szálspecifikus, a kupac pedig alkalmazásfüggő. A verem fontos figyelembe venni a kivételkezelésnél és a szál végrehajtásakor. Minden szál veremhez jut, míg az alkalmazáshoz általában csak egy halom van (bár nem ritka, hogy több halom van a különböző típusú allokációkhoz). Futás közben, ha az alkalmazásnak több halomra van szüksége, akkor szabad memóriából tud lefoglalni memóriát, és ha a veremnek memóriára van szüksége, akkor szabad memóriából lefoglalt memóriát rendelhet az alkalmazáshoz. Még, részletesebben itt és itt van megadva. Most jöjjön a kérdésére adott válaszokra. Mennyire ellenőrzi őket az operációs rendszer vagy a nyelv futásideje? Az operációs rendszer a rendszer létrehozásakor minden rendszerszintű szálhoz lefoglalja a veremet. Az operációs rendszert általában a nyelv futásideje hívja meg, hogy lefoglalja a halmot az alkalmazás számára. További információk itt találhatók. Mi a hatályuk? Már fent van adva. "Akkor használhatja a verem, ha pontosan tudja, hogy mennyi adatot kell lefoglalnia a fordítási idő előtt, és ez nem túl nagy. Használhatja a kupacot, ha nem tudja pontosan, mennyi adatra lesz szüksége futás közben, vagy sok adatot kell elosztania. " További információ itt található. Mi határozza meg mindegyik méretét? A verem méretét az operációs rendszer határozza meg egy szál létrehozásakor. A halom méretét az alkalmazás indításakor állítják be, de nőhet, ha helyre van szükség (a lefoglaló több memóriát kér az operációs rendszertől). Mi teszi az embert gyorsabbá? A veremkiosztás sokkal gyorsabb, mivel valójában csak a veremmutató mozgatása. A memóriakészletek használatával összehasonlítható teljesítmény érhető el a halomelosztásból, de ez enyhe hozzáadott összetettséggel és saját fejfájással jár. A verem és a kupac nem csak teljesítmény szempont; sokat elárul a tárgyak várható élettartamáról is. Részletek itt találhatók. | Rendben, egyszerűen és rövid szavakkal: rendelt és nem rendelt ...! Verem: A rakatban a dolgok egymásra kerülnek, ami azt jelenti, hogy gyorsabb és hatékonyabb lesz a feldolgozásuk! ... Tehát mindig van egy mutató, amely az adott elemre mutat, a feldolgozás is gyorsabb lesz, kapcsolat van az elemek között is! ... Halom: Nincs sorrend, a feldolgozás lassabb lesz, és az értékeket összekeverik, nincs külön sorrend vagy index ... vannak véletlenszerűek és nincs kapcsolat közöttük ... így a végrehajtás és a használati idő változhat Az alábbi képet is létrehozom, hogy megmutassam, hogyan nézhetnek ki: | verem, halom és az egyes folyamatok adatai a virtuális memóriában: | Az 1980-as években a UNIX nyusziként terjedt el, a nagy cégek saját maguk forgatták. Az Exxonnak volt ilyen, akárcsak a történelemnek elvesztett tucatjai. A memória elrendezése a sok megvalósító belátása szerint történt. Egy tipikus C programot síkban állítottak össze a memóriában a növekedés lehetősége a brk () érték megváltoztatásával. Jellemzően a HEAP éppen e brk érték alatt volt és a brk növelése növelte a rendelkezésre álló kupac mennyiségét. Az egyetlen STACK tipikusan a HEAP alatti terület volt, amely a memória traktusa volt nem tartalmaz semmi értékeset a következő rögzített memóriablokk tetejéig. Ez a következő blokk gyakran CODE volt, amelyet a veremadatok felülírtak korának egyik leghíresebb csapkodásában. Az egyik tipikus memóriablokk a BSS volt (nulla blokk)értékek) amelyet véletlenül nem nulláztak le az egyik gyártó kínálatában. A másik az inicializált értékeket tartalmazó DATA volt, beleértve a karakterláncokat és a számokat. A harmadik a CRT (C futási idő), a fő, a függvények és a könyvtárakat tartalmazó CODE volt. A virtuális memória megjelenése a UNIX-ban sok korlátot megváltoztat. Nincs objektív oka annak, hogy ezeknek a blokkoknak egymásnak kell lenniük, vagy rögzített méretű, vagy most egy bizonyos módon rendelt. Természetesen a UNIX előtt a Multics volt, amely nem szenvedett ezektől a korlátoktól. Itt egy vázlat mutatja be annak a korszaknak az egyik memóriaelrendezését. | Pár cent: azt hiszem, jó lesz grafikusan és egyszerűbben rajzolni a memóriát: Nyilak - megmutatja, hogy hol növekszik a verem és a kupac, a folyamat verem méretének van-e korlátja, amelyet az operációs rendszer határoz meg, a szál verem méretének korlátozását a szál létrehozásának API paraméterei általában tartalmazzák. Halom általában a folyamat maximális virtuális memória méretével korlátozódik, például 32 bites 2–4 GB-ra. Olyan egyszerű módszer: a folyamathalom általános a folyamat és a benne lévő összes szál esetében, memóriaallokációra használatos, hasonló esetben, mint például a malloc (). A Stack gyors memória a szokásos esetekben a függvény visszatérési mutatóinak és változóinak tárolására, amelyeket a függvényhívás, a helyi függvény változók paramétereként dolgoznak fel. | Mivel néhány válasz nem vált be, hozzájárulok az atkámhoz. Meglepő módon senki sem említette, hogy többszörös (azaz nem az operációs rendszer szintű szálak számához kapcsolódik) hívási halom található nemcsak egzotikus nyelveken (PostScript) vagy platformokon (Intel Itanium), hanem szálakban, zöld szálakban is és néhány korutin megvalósítása. A szálak, a zöld szálak és a koroutinok sok szempontból hasonlóak, ami sok zavart okoz. A rostok és a zöld szálak közötti különbség az, hogy az előbbi kooperatív multitasking-ot használ, míg az utóbbi vagy kooperatív, vagy megelőző jellegű lehet (vagy akár mindkettő). A rostok és a koroutinok megkülönböztetését lásd itt. Mindenesetre mind a szálak, mind a zöld szálak, mind a koroutinok célja több funkció egyidejű végrehajtása, de nem párhuzamosan (lásd ezt a SO kérdést a megkülönböztetésért) egyetlen operációs rendszer szintű szálon belül, ide-oda átadva a vezérlést szervezetten. Rostok, zöld szálak vagy koroutinok használatakor általában külön köteg van funkciónként. (Technikailag nemcsak egy verem, hanem egy teljes végrehajtási kontextus függvényenként. A legfontosabb: a CPU regisztrál.) Minden szálhoz annyi verem van, ahány párhuzamosan futó funkció, és a szál változik az egyes funkciók végrehajtása között a program logikája szerint. Amikor egy függvény a végéig fut, a verem megsemmisül. Tehát a veremek száma és élettartama dinamikus, és nem az OS-szintű szálak száma határozza meg! Ne feledje, hogy azt mondtam, hogy "általában külön verem van függvényenként". Halmozott és halmtalan megvalósítású Couroutines is van. A legtöbb figyelemre méltó halmozott C ++ megvalósítás a Boost.Coroutine és a Microsoft PPL async / await. (A C ++ 17 által javasolt C ++ funkciók (más néven "aszinkron és várakozás") azonban valószínűleg verem nélküli koroutint használnak.) Szálakra vonatkozó javaslat érkezik a C ++ szabványos könyvtárhoz. Emellett vannak harmadik féltől származó könyvtárak is. A zöld szálak rendkívül népszerűek olyan nyelveken, mint a Python és a Ruby. | Van mit megosztanom, bár a főbb pontokat már lefedtük. Kazal Nagyon gyors hozzáférés. RAM-ban tárolva. A függvényhívások ide kerülnek betöltésre a helyi változókkal és az átadott funkcióparaméterekkel együtt. A hely automatikusan felszabadul, ha a program kilép a hatókörből. Szekvenciális memóriában tárolva. Halom Lassú hozzáférés a Stackhez képest. RAM-ban tárolva. A dinamikusan létrehozott változókat itt tároljuk, ami később használat után megköveteli a lefoglalt memória felszabadítását. Tárolja, bárhol történik a memória-allokáció, ahová a mutató mindig hozzáférhet. Érdekes megjegyzés: Ha a függvényhívásokat halmban tárolták volna, akkor ez két rendetlen pontot eredményezett: A veremben történő szekvenciális tárolás miatt a végrehajtás gyorsabb. A halomban történő tárolás óriási időfogyasztást eredményezett, így az egész program lassabban futtatható. Ha a funkciókat halmban tárolták (rendetlen tárhely mutatott mutatóval), nem lett volna mód visszatérni a hívó címére (amelyet a memória szekvenciális tárolása miatt ad meg a verem). | Azta! Ennyi válasz, és nem hiszem, hogy egyiküknek lett volna igaza ... 1) Hol és mik vannak (fizikailag a valódi számítógép memóriájában)? A verem olyan memória, amely a program képéhez rendelt legmagasabb memóriacímként kezdődik, majd onnan csökken az értéke. Foglalt a meghívott függvényparaméterekre és a függvényekben használt összes ideiglenes változóra. Két halom van: nyilvános és magán. A privát kupac egy 16 bájtos (64 bites programok) vagy egy 8 bájtos (32 bites programok) határon kezdődik a program utolsó bájtja után, majd növekszikérték onnan. Alapértelmezett kupacnak is nevezik. Ha a privát kupac túl nagy lesz, akkor átfedésben lesz a verem területével, csakúgy, mint a verem átfedésben a halommal, ha túl nagy lesz. Mivel a verem magasabb címről indul, és lefelé halad alacsonyabb címre, megfelelő hackeléssel elérheti, hogy a verem olyan nagy legyen, hogy túllépje a privát kupac területét és átfedje a kódterületet. A trükk ekkor az, hogy elég átfedje a kódterületet, amelyet bekapcsolhat a kódba. Kicsit trükkös ezt megtenni, és kockáztatja a program összeomlását, de egyszerű és nagyon hatékony. A nyilvános halom a saját memóriaterületén található, a program képterületén kívül. Ezt a memóriát szivattyúzzák le a merevlemezre, ha szűkösek a memóriaforrások. 2) Mennyire ellenőrzi őket az operációs rendszer vagy a nyelv futásideje? A veremet a programozó irányítja, a privát kupacot az operációs rendszer kezeli, a nyilvános kupacot pedig senki sem ellenőrzi, mert ez operációs rendszer szolgáltatás - Ön kéréseket küld, és vagy megadják, vagy elutasítják őket. 2b) Mi a hatályuk? Ezek mind globálisak a program szempontjából, de tartalmuk lehet magán, nyilvános vagy globális. 2c) Mi határozza meg mindegyik méretét? A verem és a privát kupac méretét a fordító futásidejű beállításai határozzák meg. A nyilvános kupac futás közben inicializálódik egy méretparaméter segítségével. 2d) Mitől gyorsabb az ember? Nem gyors, hanem hasznosak. Az, hogy a programozó hogyan használja fel őket, meghatározza, hogy "gyorsak" vagy "lassúak" REF: https://norasandler.com/2019/02/18/Write-a-Compiler-10.html https://docs.microsoft.com/en-us/windows/desktop/api/heapapi/nf-heapapi-getprocessheap https://docs.microsoft.com/en-us/windows/desktop/api/heapapi/nf-heapapi-heapcreate | Sok válasz helyes, mint fogalom, de meg kell jegyeznünk, hogy egy veremre van szükség a hardver (azaz mikroprocesszor) számára, hogy lehetővé tegye az alprogramokat (CALL az összeállítási nyelven ..). (Az OOP srácok módszereknek fogják nevezni) A veremben elmenti a visszatérési címeket, és a hívás → push / ret → pop kezelése közvetlenül hardveren történik. A verem segítségével átadhatja a paramétereket .. még akkor is, ha ez lassabb, mint a regiszterek használata (mondana egy mikroprocesszoros guru vagy egy jó 1980-as BIOS-könyv ...) Verem nélkül egyetlen mikroprocesszor sem működhet. (programot még összeállítási nyelven sem tudunk elképzelni alprogramok / függvények nélkül) A kupac nélkül képes. (Az összeállítási nyelvű program nélkül is működhet, mivel a kupac operációs rendszer-fogalom, mint malloc, vagyis OS / Lib hívás. A veremhasználat gyorsabb, mint: Hardveres, sőt a push / pop is nagyon hatékony. A malloc megköveteli a kernel módba való belépést, a lock / szemafor (vagy más szinkronizációs primitívek) használatával végrehajt egy kódot, és kezel néhány struktúrát, amelyek szükségesek a kiosztás nyomon követéséhez. | A halom a dinamikusan lefoglalt memória területe, amelyet az operációs rendszer vagy a memóriakezelő könyvtár automatikusan kezel. Bármikor kioszthat egy blokkot, és bármikor felszabadíthatja. A halomallokációhoz teljes nyilvántartást kell vezetni arról, hogy milyen memória van allokálva és mi nem, valamint bizonyos általános karbantartásokat kell végrehajtani a széttagoltság csökkentése érdekében, a szomszédos memóriaszegmensek megtalálásához elég nagynak ahhoz, hogy elférjenek a kért méretnél, stb. A memória bármikor felosztható, így szabad hely marad. A halom növekedésével az új blokkokat gyakran az alacsonyabb címek és a magasabb címek között osztják ki. Tehát úgy gondolhat a halomra, mint egy memóriablokk-halomra, amely a memória kiosztásakor növekszik. Ha a halom túl kicsi a kiosztáshoz, a méret gyakran növelhető, ha több memóriát szerez az alapul szolgáló operációs rendszerből. A kupacból lefoglalt memória addig marad allokálva, amíg az alábbiak valamelyike bekövetkezik: A memória felszabadul A program leáll Kazal: A számítógépes RAM-ban tárolják, ugyanúgy, mint a kupacot. A veremben létrehozott változók kiesnek a hatókörből, és automatikusan elosztásra kerülnek. Sokkal gyorsabban allokálható a halom változóihoz képest. A paraméterek átadásához használt helyi adatokat, visszaküldési címeket tárolja. Túlcsordulhat a verem, ha a verem túl nagy részét használják (főleg végtelen vagy túl mély rekurzióból, nagyon nagy allokációk). Akkor használja a verem, ha pontosan tudja, mennyi adatra van szüksége kiosztani a fordítási idő előtt, és ez nem túl nagy. Általában a program maximális mérete már meg van határozva indul. Halom: A számítógép RAM-ban tárolva, csakúgy, mint a verem. A C ++ - ban a halom változóit manuálisan és soha nem kell megsemmisíteni kiesnek a hatókörből. Az adatok törléssel, törléssel [] vagy szabaddá tételével szabaddá válnak. Lassabban allokálható a verem változóihoz képest. Igény szerint adatblokk kiosztására szolgál a program számára. Szétaprózódhat, ha sok a kiosztás és üzletkötések. C ++ vagy C esetén a kupacon létrehozott adatokra mutatók mutatnak és új vagy malloc-al allokálják. Előfordulhat allokációs hiba, ha túl nagy puffert igényel kell kiosztani. tehasználná a kupacot, ha nem tudja pontosan, mennyi adatot tartalmaz futási időben, vagy ha sok adatot kell lefoglalnia. Felelős a memória szivárgásáért. | A verem lényegében könnyen hozzáférhető memória, amely egyszerűen kezeli az elemeit mint - jól verem. Csak azok az elemek kerülhetnek a verembe, amelyek mérete előre ismert. Ez a számok, húrok, logikai értékek esetében érvényes. A halom olyan elemek emléke, amelyeknek előre nem határozható meg pontos méret és felépítés. Mivel az objektumok és tömbök mutálhatók és futás közben változtatni kell, be kell menniük a kupacba. Forrás: Academind | A CPU-verem és a halom fizikailag kapcsolatban állnak azzal, hogy a CPU és a regiszterek hogyan működnek a memóriával, hogyan működnek a gép-összeszerelő nyelvek, és nem maguk a magas szintű nyelvek, még akkor sem, ha ezek a nyelvek apró dolgokról dönthetnek. Minden modern CPU "ugyanazon" mikroprocesszor elmélettel működik: mindegyik az úgynevezett "regisztereken" alapszik, és néhány a "verem" számára nyújt teljesítményt. Valamennyi CPU-nak a kezdetektől fogva vannak veremregiszterei, és mindig is itt voltak, mint beszélni, mint tudom. Az összeállítási nyelvek a kezdetek óta megegyeznek, a változatok ellenére ... egészen a Microsoftig és annak közbenső nyelvéig (IL), amelyek megváltoztatták a paradigmát az OO virtuális gép összeállításának nyelvével. Tehát a jövőben képesek leszünk néhány CLI / CIL CPU-ra (az MS egyik projektje). A CPU-k veremregiszterekkel rendelkeznek, hogy felgyorsítsák a memóriák elérését, de azok korlátozottak ahhoz, hogy más regisztereket használhassanak, hogy teljes hozzáférést kapjanak a processzor összes rendelkezésre álló memóriájához. Ezért beszéltünk a verem és a halom kiosztásáról. Összefoglalva, és általában véve a halom összeszorított és lassú, és "globális" példányokra és objektumokra vonatkozik, mivel a verem kevés és gyors, valamint a "helyi" változók és hivatkozások (rejtett mutatók elfelejteni kezelni őket). Tehát amikor az új kulcsszót egy módszerben használjuk, akkor a veremben létrejön a hivatkozás (int), de ha emlékszem, az objektum és annak minden tartalma (értéktípusok, valamint objektumok) a kupacban jön létre. De a veremben helyi elemi értéktípusok és tömbök jönnek létre. A memóriaelérés különbsége a cellák hivatkozási szintjén van: a halom, a folyamat teljes memóriájának kezelése a CPU regiszterek kezelése szempontjából bonyolultabb megoldást igényel, mint a verem, amely lokálisan a címzés szempontjából "több", regisztrációt használjuk alapcímként, ha emlékszem. Ezért van az, hogy amikor nagyon hosszú vagy végtelen visszatérő hívások vagy hurkok vannak, akkor gyorsan túlcsordulunk, anélkül, hogy megfagynánk a rendszert a modern számítógépeken ... C # Heap (ing) Vs Stack (ing) In .NET Verem vs kupac: Ismerje meg a különbséget Statikus osztályú memória-allokáció, ahol tárolva van C # Mi és hol van a verem és a kupac? https://en.wikipedia.org/wiki/Memory_management https://en.wikipedia.org/wiki/Stack_register A közgyűlés nyelvi erőforrásai: Összeszerelés programozási bemutató Intel® 64 és IA-32 Architectures szoftverfejlesztői kézikönyvek | Köszönöm az igazán jó beszélgetést, de igazi noobként kíváncsi vagyok, hol tartják az utasításokat? A KEZDETBEN a tudósok két architektúra között döntöttek (von NEUMANN, ahol mindent DATA-nak tekintenek, és HARVARD-nak, ahol a memória egy területét lefoglalták az utasításoknak, egy másikat pedig az adatoknak). Végül a von Neumann-tervvel mentünk, és most mindent „ugyanannak” tekintenek. Ez megnehezítette számomra, amikor szerelést tanultam https://www.cs.virginia.edu/~evans/cs216/guides/x86.html mert regiszterekről és veremmutatókról beszélnek. A fentiekben minden a DATA-ról szól. Azt hiszem, hogy mivel az utasítás egy meghatározott dolog, amelynek sajátos memóriaterülete van, a veremre kerül, és így az összes, az összeállításban tárgyalt regiszter a veremben van. Természetesen akkor jött az objektum-orientált programozás, utasításokkal és adatokkal, amelyek dinamikus struktúrába érkeztek, így most az utasításokat is a kupacban tartanák? | Nagyon aktív kérdés. Nyerjen 10 hírnevet a kérdés megválaszolásához. A jó hírnév követelménye megvédi ezt a kérdést a spamektől és a nem válaszolóktól. Nem a keresett válasz? Böngésszen a memória-kezelő verem nyelv-agnosztikus halom dinamikus memória-allokációval címkézett többi kérdésében, vagy tegye fel a saját kérdését.